package com.mh.system.service.impl;

import com.mh.common.utils.UserConstants;
import com.mh.system.domain.SysMenu;
import com.mh.system.domain.vo.MetaVO;
import com.mh.system.domain.vo.RouterVO;
import com.mh.system.mapper.SysMenuMapper;
import com.mh.system.service.SysMenuService;
import org.apache.ibatis.annotations.Lang;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.awt.*;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@Service
public class SysMenuServiceImpl implements SysMenuService {

    @Autowired
    private SysMenuMapper menuMapper;

    //根据用户ID查询菜单权限, 返回 system/role/index
    @Override
    public Set<String> selectPermsByUserId(Long userId) {
        Set<String> perms = menuMapper.selectPermsByUserId(userId);
        return perms;
    }

    //根据userId, 查询菜单的全部信息
    @Override
    public List<SysMenu> selectMenuTreeByUserId(Long userId) {

        List<SysMenu> menus = null;

        //如果是admin管理员的话,返回全部信息
        if(userId != null && userId  == 1L){
            menus = menuMapper.selectMenuTreeAll();
        } else{ //否则根据id查询对应的信息
            menus = menuMapper.selectMenuTreeByUserId(userId);
        }

        //封装子菜单
        return getChildPerms(menus,0);
    }
    //子方法1: 获取一级菜单: 传入userid对应的所有权限,从一级菜单0开始遍历
    private List<SysMenu> getChildPerms(List<SysMenu> menus, int parentId) {
        List<SysMenu> returnList = new ArrayList<>();
        //传入的menu集合转为流
        menus.stream()
                //筛选出一级菜单
                .filter(m-> m.getParentId() == parentId)
                //对menus筛选出来的集合中的每个元素都遍历-->最顶级菜单
                .forEach(m -> {
                    //再调用下面的方法-->传入形参的menu(mapper根据ID查出的结果),筛选出的每个元素
                    recursionFn(menus,m);
                    //筛选出的每个元素都加进集合中,并返回
                    returnList.add(m);
                });
        return returnList;
    }
    //子方法2: 递归获取子级菜单: 传入所有权限,筛选出的每一个一级菜单
    private void recursionFn(List<SysMenu> menus, SysMenu menu) {

        //得到子节点列表,保存到父菜单的children中
        //再调用下面的方法, 获取二级菜单-->传入所有权限,一级菜单对象
        List<SysMenu> childList = getChildList(menus,menu);
        //获取到的二级菜单赋值给实体类Menu
        menu.setChildren(childList);

        //二级菜单集合遍历, 每个元素再调用子方法3-->再获取三级菜单
        for (SysMenu childMenu : childList) {
            //判断子节点下是否还有子节点
            if(!getChildList(menus, childMenu).isEmpty()){
                recursionFn(menus, childMenu); //有三级菜单的话再调用自己,形成递归调用,直到没有下一级
            }
        }
    }
    //子方法3: 获取二级菜单: 传入所有权限,一级菜单对象
    private List<SysMenu> getChildList(List<SysMenu> menus, SysMenu menu) {
        List<SysMenu> subMenus = menus.stream()
                //筛选出: 所有权限中parentID = 一级菜单的menuID --> 表示那个parentID对应的是一级的子级
                .filter(sub -> sub.getParentId().longValue() == menu.getMenuId().longValue())
                .collect(Collectors.toList());
        return subMenus;
    }

    //封装为前端所需的菜单导航列表
    @Override
    public List<RouterVO> buildMenus(List<SysMenu> menus) {

         List<RouterVO> routers = new ArrayList<>();

         for (SysMenu menu : menus) {
             RouterVO routerVO = new RouterVO();

             //共同字段: 一级目录&子级目录都有的5个共同属性-->name,path,component,hidden,meta
             routerVO.setName(getRouterName(menu)); //第一个字母需要大写 --> 提取为一个方法,此处调用
             routerVO.setPath(getRouterPath(menu)); //父菜单路径前要加上"/" --> 提取为一个方法,此处调用
             routerVO.setComponent(getComponent(menu)); //导航菜单
             //"1".equals(0)(显示)-->false-->前端显示hidden:false(显示)
             routerVO.setHidden("1".equals(menu.getVisible())); //数据库中显示: 菜单状态(0显示 1隐藏)
             /** Meta有3个属性,通过构造器创建对象
              *  title: "系统管理" --> 来自MenuName字段
              *  icon: "system" --> 来自Icon字段
              *  noCache: false(表示缓存) --> 来自isCache字段 --> "1"equals(0)(缓存)-->false
              *  数据库中: isCache 是否缓存（0缓存 1不缓存）
              */
             routerVO.setMeta(new MetaVO(menu.getMenuName(),menu.getIcon(),"1".equals(menu.getIsCache())));

             //一级目录独有字段: alwaysShow,redirect,children
             //因此下面判断是一级目录才会设值独有字段
             List<SysMenu> children = menu.getChildren();
             //子级集合非空 + 大小>0 + 类型为"M" --> 表示是一级(父)
             if (children != null && children.size() > 0 && UserConstants.TYPE_DIR.equals(menu.getMenuType())) {
                 routerVO.setAlwaysShow(true);
                 routerVO.setRedirect("noRedirect");
                 routerVO.setChildren(buildMenus(children)); //调用自己,递归调用设置子菜单
             }
             routers.add(routerVO);
         }
        return routers;
    }
    //子方法1: 第一个字母需要大写
    private String getRouterName(SysMenu menu) {
        //首字母大写: 形参中获取到path, 再通过StringUtils.capitalize把它的首字母转为大写
        return StringUtils.capitalize(menu.getPath());
    }
    //子方法2: 父菜单路径前要加上"/"
    private String getRouterPath(SysMenu menu) {
        String path = menu.getPath();

        //.intValue(): 将Integer对象转换为int基本数据类型
        //如果.getParentId()为null, 如下比较可能会抛异常, 为了避免潜在的问题, 这样写比较安全

        //public static final String TYPE_DIR = "M";
        //parentId为0 + .getMenuType()为M --> 表示一级目录 --> 才会在path前面加上"/"
        if (menu.getParentId().intValue() == 0 && UserConstants.TYPE_DIR.equals(menu.getMenuType())) {
            path = "/"+path;
        }
        return path; //不是一级目录的话,直接返回path(无需加上"/")
    }
    //子方法3: 获取组件信息(左侧目录结构)
    private String getComponent(SysMenu menu) {

        //public final static String LAYOUT = "Layout";
        //component 组件 ; layout 布局

        //情况1: parentId=0 & component=null --> 默认为此情况(一级目录)
        String component = UserConstants.LAYOUT;

        //情况2: component≠null, 有值 --> 直接返回值
        if (! StringUtils.isEmpty(menu.getComponent())) {
            component = menu.getComponent();

            //情况3: parentId≠0 & menuType='M' --> 相当于是二级目录,有一个"日志管理"
        } else if (menu.getParentId().intValue() != 0 && UserConstants.TYPE_DIR.equals(menu.getMenuType())) {
            //public final static String PARENT_VIEW = "ParentView";
            //接口文档要求二级目录返回"ParentView"
            component = UserConstants.PARENT_VIEW;
        }
        return component;
    }
}
